#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#include <unistd.h> 
#include <iostream>
#include <fstream>

#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>

#define SERVER_PORT 1234
#define QUEUE_SIZE 5

using namespace std;

string execute(string cmd) {
	cmd = "exec/" + cmd;
    FILE* pipe = popen(cmd.c_str(), "r");
    if (!pipe)
	{
		return "Execution error!";
	}
    char buffer[128];
    string result = "";
    while(fgets(buffer, 128, pipe) != NULL)
	{
    	result += buffer;
    }
    pclose(pipe);
    return result;
}

int createSocket()
{
	struct sockaddr_in serverAddress;
	int socketFileDescriptor;
	int socketOption = 1;
	
	// socket address structure
	memset(&serverAddress, 0, sizeof(struct sockaddr));
	serverAddress.sin_family      = AF_INET;
	serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
	serverAddress.sin_port        = htons(SERVER_PORT);

	// create a socket
	socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
	if (socketFileDescriptor < 0)
	{
		perror("ERROR opening socket");
		exit(1);
	}
	
	// disable waiting for socket release
	setsockopt(socketFileDescriptor, 
		SOL_SOCKET, SO_REUSEADDR, 
		(char*)&socketOption, 
		sizeof(socketOption));

	// bind a name to a socket
	if (bind(socketFileDescriptor, 
		(struct sockaddr*)&serverAddress, 
		sizeof(struct sockaddr)) < 0)
	{
		perror("ERROR on binding");
		exit(1);
	}

	// specify queue size
	if (listen(socketFileDescriptor, QUEUE_SIZE) < 0)
	{
		perror("ERROR on setting queue size");
	}
	
	return socketFileDescriptor;
}

bool swrite(string str, int clientSocket)
{
	int counter = 0, 
		limit = str.size();
	
	while( counter < limit )
	{
		str = str.substr(counter, limit - counter);
		counter += write(clientSocket, str.c_str(), str.size());
		if(counter == -1)
		{
			return true;
		}
	}
	return false;
}

string sread(int clientSocket)
{
	char buffer[100000];
	char tempBuffer[10];
	int count = sizeof(tempBuffer);
	buffer[0] = '\0';
	while( count == sizeof(tempBuffer) )
	{
		count = read(clientSocket, tempBuffer, sizeof(tempBuffer));
		strncat(buffer, tempBuffer, count);
	}
	return buffer;
}

void sendCommandList(int clientSocket)
{
	char* a = new char();
	string commands;
	ifstream commandsFile;
	
	commandsFile.open("execParams.txt");
	while( !commandsFile.eof() )
	{
		commandsFile.read(a, 1);
		commands += a[0];
	}
	commandsFile.close();
	
	swrite(commands, clientSocket);
}

void handleClientConnection(int clientSocket)
{
	string clientCommand;
	int commandNumber;
	string result;
	swrite("New client\n",1);
	while(true)
	{
		clientCommand = sread(clientSocket);
		swrite("Recived: " + clientCommand +"\n",1);
		commandNumber = clientCommand[0];
		switch(commandNumber - '0')
		{
			case 0:
				swrite("Send list\n",1);
				sendCommandList(clientSocket);
				break;
			case 1:
				clientCommand = clientCommand.substr(2);
				swrite("Exec cmd " + clientCommand + "\n",1);
				swrite(execute(clientCommand), clientSocket);
				break;
			case 2:
				swrite("Disconnect client\n",1);
				return;
				break;
			default:
				perror("ERROR on userCommand");
				return;
				break;
		}
	}
}

int main()
{
	int socketFD, clientSocketFD;
	struct sockaddr_in clientAddress;
	int pid = 1;
	
	socketFD = createSocket();
	
	int clientAddressSize = sizeof(struct sockaddr);
	
	while (1) 
	{
		clientSocketFD = accept(socketFD, 
			(struct sockaddr *) &clientAddress, 
			(socklen_t *) &clientAddressSize);
		if (clientSocketFD < 0)
		{
			perror("ERROR on accept");
			exit(1);
		}
		
		// connection handling
		pid = fork();
		if (pid < 0)	// error handle
		{
			perror("ERROR on fork");
			close(clientSocketFD);
			exit(1);
		}
		else if (pid == 0)  // client handling process
		{
			close(socketFD);
			handleClientConnection(clientSocketFD);
			close(clientSocketFD);
			exit(0);
		}
		else	// mother process
		{
			close(clientSocketFD);
		}
	}
	return 0;
} 